
///
/// This is the implementation of the Client class
///

#include "stdlib.h"
#include "stdio.h"
#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cassert>
#include "common.h"
#include "Client.h"

Client* Client::mClient = NULL;

int Client::Initialize(const char* ip, const char* port)
{
	struct addrinfo temp, *res;

	memset(&temp, 0, sizeof(temp));		temp.ai_family = AF_UNSPEC;		temp.ai_socktype = SOCKET_TYPE;
	
	int retVal, numBytes;

	if((retVal = getaddrinfo(ip, /*TFTP_PORT_STR*/port, &temp, &res)) != 0)
	{
		std::cout << "getaddrinfo() error: " << gai_strerror(retVal) << std::endl;
		exit(1);
	}

	std::cout << "Connecting to server..." << std::endl;
	std::cout << "IP Address: " << ip << std::endl;
	std::cout << "Port Number: " << /*TFTP_PORT_STR*/port << std::endl;

	//create client socket
	mMainFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if(mMainFD == -1)
	{
		perror("socket() error: ");
		exit(2);
	}

	char str[] = "Howdy!";
	if((numBytes = sendto(mMainFD, str, strlen(str), 0, res->ai_addr,
							res->ai_addrlen)) == -1)
	{
		perror("sendto() error: ");
		exit(1);
	}

	std::cout << "Connected successfully to server!" << std::endl;

	freeaddrinfo(res);

	return 0;
}

int Client::Execute()
{
#if 0
	//initialize FD set for select()
	fd_set readFDS;
	FD_ZERO(&readFDS);
	FD_ZERO(&mConnectedFDS);
	
	FD_SET(mMainFD, &mConnectedFDS);
	FD_SET(STDIN, &mConnectedFDS); //for command line control of server
	mMaxFD = std::max(STDIN, mMainFD);

	Handler msgHandler(mMainFD, CLIENT);

	if(SendJoin() == -1)
	{
		std::cout << "Couldn't send JOIN... exiting!" << std::endl;
		close(mMainFD);
		exit(2);
	}

	struct timeval tv;
	tv.tv_sec = 300;	tv.tv_usec = 0; // 5 min timeout

	int retVal;

	while(1)
	{
		readFDS = mConnectedFDS;
		retVal = select(mMaxFD+1, &readFDS, NULL, NULL, NULL);
		if(retVal == -1)
		{
			perror("select() error: ");
			exit(3);
		}
		else if(retVal == 0)
		{
			std::cout << "User found away for too long!!! Exiting..." << std::endl;
			return 0;
		}

		for(int i = 0; i <= mMaxFD; ++i)
		{
			if(FD_ISSET(i, &readFDS))
			{
				if(i == STDIN)
				{
					//handle std input commands
					static char chatBuf[MSG_BUF_SIZE];
					if(!fgets(chatBuf, MSG_BUF_SIZE, stdin))
					{
						if(ferror(stdin))
						{
							perror("fgets(stdin) error: ");
							close(mMainFD);
							return 3;
						}
					}

					static const std::string quitStr("QUIT\n");
					if(quitStr.compare(chatBuf) == 0)
					{
						//QUIT received from user, send LEAVE and close socket
						SendLeave();
						close(mMainFD);
						return 0;
					}

					//SEND chat to server
					if(SendChat(chatBuf) == -1)
					{
						std::cout << "Failed to send chat msg to server" << std::endl;
						close(mMainFD);
						exit(3);
					}
				}
				else if(i == mMainFD)
				{

					int byteCount = recv(mMainFD, mMsgBuf, MSG_BUF_SIZE, 0);
					
					if(byteCount == -1)
					{
						perror("recv() error: ");
						close(mMainFD);
						exit(3);
					}
					else if(byteCount == 0)
					{
						std::cout << "Server closed the socket!" << std::endl;
						close(mMainFD);
						exit(3);
					}

					if(msgHandler.HandleMessage(mMsgBuf, byteCount) == -1)
					{
						std::cout << "Client couldn't interpret the message" << std::endl;
						continue;
					}
				}
				else
				{
					assert(false);
				}
			}
		}
	}

	close(mMainFD);
	return 0;
#endif
}
#if 0
int Client::SendJoin()
{
	const unsigned int userNameLength = mUserName.length();

	StructAttribute attribute;
	attribute.type = ATTR_TYPE_USERNAME;
	attribute.length = userNameLength + ATTR_SIZE;
	
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_JOIN;
	header.length = attribute.length + HEADER_SIZE;
	
	char* dest = &mMsgBuf[0];

	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy(dest, mUserName.c_str(), userNameLength);
	dest += userNameLength;

	//sanity check
	assert((dest - mMsgBuf) == header.length);

	/*std::cout << "Sending " << header.length << " byte message to server: " << std::endl;
	for(unsigned int i = 0; i < header.length; ++i)
	{
		printf("%x ", mMsgBuf[i]);
	}
	std::cout << std::endl;*/

	return send(mMainFD, mMsgBuf, header.length, 0);
}

int Client::SendChat(const char* chatMsg)
{
	const unsigned int messageLength = std::max(
		static_cast<int>(strlen(chatMsg)), MAX_CHAT_TEXT_SIZE);
	
	StructAttribute attribute;
	attribute.type = ATTR_TYPE_MESSAGE;
	attribute.length = messageLength + ATTR_SIZE;
	
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_SEND;
	header.length = attribute.length + HEADER_SIZE;
	
	//modified as below

	char* dest = &mMsgBuf[0];
	
	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;	
	
	memcpy(dest, chatMsg, messageLength);
	dest += messageLength;

	//sanity check
	assert((dest - mMsgBuf) == header.length);

	return send(mMainFD, mMsgBuf, header.length, 0);

}

int Client::SendLeave()
{
	
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_LEAVE;
	header.length = HEADER_SIZE;
	
	
	char* dest = &mMsgBuf[0];
	
	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	
	return send(mMainFD, mMsgBuf, header.length, 0);
}

///////////////////////
///// HANDLERS ////////
///////////////////////
int Client::HandleNakCommand()
{
	close(mMainFD);
	exit(0);
}
#endif
